﻿using System;
using System.Collections.Generic;
using System.Text;
using Tracy.CoreLib.Data;
using Tracy.CoreLib;
using System.Threading;

namespace Tracy.Engine
{
    /// <summary>
    /// 标签管理器
    /// </summary>
    public class TagManager
    {
        private const int RWLOCK_TIMEOUT = 1000;
        private static ReaderWriterLock rwLockProperty = new ReaderWriterLock();
        private static ReaderWriterLock rwLockRelationship = new ReaderWriterLock();
        private static Object s_lock_engine = new Object();//用于lock块
        private TracyPropertyProvider _propertyProvider;
        /// <summary>
        /// 属性
        /// </summary>
        private TracyPropertyProvider PropertyProvider
        {
            get
            {
                if (_propertyProvider == null)
                {
                    lock (s_lock_engine)
                    {
                        if(_propertyProvider == null) _propertyProvider = new TracyPropertyProvider();
                    }
                }
                return _propertyProvider;
            }
        }

        private TracyRelationshipProvider _relationshipProvider;
        /// <summary>
        /// 关系
        /// </summary>
        private TracyRelationshipProvider RelationshipProvider
        {
            get
            {
                if (_relationshipProvider == null)
                {
                    lock (s_lock_engine)
                    {
                        if (_relationshipProvider == null) _relationshipProvider = new TracyRelationshipProvider();
                    }
                }
                return _relationshipProvider; 
            }
        }

        private TracyBus _bus = null;
        /// <summary>
        /// 总线
        /// </summary>
        private TracyBus Bus
        {
            get { return _bus; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="bus">总线</param>
        public TagManager(TracyBus bus)
        {
            _bus = bus;
            try
            {
                rwLockProperty.AcquireWriterLock(RWLOCK_TIMEOUT);
                rwLockRelationship.AcquireWriterLock(RWLOCK_TIMEOUT);
                PropertyProvider.Load(Bus.TracySettings[Constants.CONST_SETTING_TRACYPROPERTY]);
                RelationshipProvider.Load(Bus.TracySettings[Constants.CONST_SETTING_TRACYRELATIONSHIP]);
            }
            finally
            {
                rwLockProperty.ReleaseWriterLock();
                rwLockRelationship.ReleaseWriterLock();
            }
            SelfCheck();
        }

        /// <summary>
        /// 检查系统属性是否正常
        /// </summary>
        private void SelfCheck()
        {
            if (GetPropertyByName(Constants.CONST_PROPERTY_FILTERSOURCE) == null)
                throw new TracyException(TracyException.EXCEPTION_SYSTEM_PARAMETER_NOT_FOUND, "属性: " + Constants.CONST_PROPERTY_FILTERSOURCE);
                
        }
        
        /// <summary>
        /// 对应类型的当前数据
        /// </summary>
        /// <param name="type">Tracy数据类型</param>
        public IManagedXmlModelCollection  GetDataByType(string type)
        {
            switch (type)
            {
                case Constants.CONST_DATA_TYPE_RESOURCEITEM: return Bus.Engine.GetCurrentResources();
                case Constants.CONST_DATA_TYPE_RSSFEEDSOURCE: return Bus.Engine.GetRssSources();
                case Constants.CONST_DATA_TYPE_RSSFILTER: return Bus.Engine.GetRssFilters();
                case Constants.CONST_DATA_TYPE_TEXTDESCRIPTION:
                    //TODO: textdescription provider
                    break;
            }
            return null;
        }

        /// <summary>
        /// ManagedXmlModelItem所对应的数据类型
        /// </summary>
        public string GetTypeName(IManagedXmlModelItem item)
        {
            if (item is ResourceItem) return Constants.CONST_DATA_TYPE_RESOURCEITEM;
            else if (item is RssFeedSource) return Constants.CONST_DATA_TYPE_RSSFEEDSOURCE;
            else if (item is RssFilter) return Constants.CONST_DATA_TYPE_RSSFILTER;
            //TODO: textdescription

            return null;
        }

        public IManagedXmlModelCollection GetProperties()
        {
            IManagedXmlModelCollection rtn;
            try
            {
                rwLockProperty.AcquireReaderLock(RWLOCK_TIMEOUT);
                rtn = ((IManagedXmlModelCollection)PropertyProvider.Items).GetCopy();

            }
            finally
            {
                rwLockProperty.ReleaseReaderLock();
            }
            return rtn;
        }

        public TracyProperty GetPropertyByName(string name)
        {
            TracyProperty rtn = null;
            try
            {
                rwLockProperty.AcquireReaderLock(RWLOCK_TIMEOUT);
                if (PropertyProvider.Items.Contains(name)) rtn = (TracyProperty)PropertyProvider.Items[name];
            }
            finally
            {
                rwLockProperty.ReleaseReaderLock();
            }
            return rtn;
        }

        public IManagedXmlModelCollection GetRelationships()
        {
            IManagedXmlModelCollection rtn;
            try
            {
                rwLockRelationship.AcquireReaderLock(RWLOCK_TIMEOUT);
                rtn = ((IManagedXmlModelCollection)RelationshipProvider.Items).GetCopy();

            }
            finally
            {
                rwLockRelationship.ReleaseReaderLock();
            }
            return rtn;
        }

        public IManagedXmlModelCollection GetRelationshipsBySource(int sourceid, string sourcetype)
        {
            IManagedXmlModelCollection rtn;
            try
            {
                rwLockRelationship.AcquireReaderLock(RWLOCK_TIMEOUT);
                rtn = RelationshipProvider.FindBySource(sourceid, sourcetype).GetCopy();

            }
            finally
            {
                rwLockRelationship.ReleaseReaderLock();
            }
            return rtn;
        }

        public IManagedXmlModelCollection GetRelationshipsBySource(IManagedXmlModelItem source)
        {
            return GetRelationshipsBySource(source.Id, GetTypeName(source));
        }

        public IManagedXmlModelCollection GetRelationshipsByTarget(int targetid, string targettype)
        {
            IManagedXmlModelCollection rtn;
            try
            {
                rwLockRelationship.AcquireReaderLock(RWLOCK_TIMEOUT);
                rtn = RelationshipProvider.FindByTarget(targetid, targettype).GetCopy();

            }
            finally
            {
                rwLockRelationship.ReleaseReaderLock();
            }
            return rtn;
        }

        public IManagedXmlModelCollection GetRelationshipsByTarget(IManagedXmlModelItem target)
        {
            return GetRelationshipsByTarget(target.Id, GetTypeName(target));
        }
        /// <summary>
        /// 获取对象所拥有的标签
        /// </summary>
        /// <param name="source">对象</param>
        /// <param name="targetType">标签数据类型</param>
        /// <returns></returns>
        public IManagedXmlModelCollection GetTaggedTargets(IManagedXmlModelItem source, string targetType)
        {
            return GetTaggedTargets(source.Id, GetTypeName(source), targetType);
        }

        /// <summary>
        /// 获取对象所拥有的标签
        /// </summary>
        /// <param name="sourceId">对象ID</param>
        /// <param name="sourceType">对象数据类型</param>
        /// <param name="targetType">标签数据类型</param>
        public IManagedXmlModelCollection GetTaggedTargets(int sourceId, string sourceType, string targetType)
        {
            IManagedXmlModelCollection rtn = new ManagedXmlModelCollection();
            IManagedXmlModelCollection relationships = GetRelationshipsBySource(sourceId, sourceType);
            foreach (TracyRelationship r in relationships)
            {
                if (targetType == r.TargetType) rtn.Add(GetDataByType(targetType).GetItemById(r.TargetId));
            }
            return rtn;
        }

        /// <summary>
        /// 添加标签
        /// </summary>
        /// <param name="property">属性</param>
        /// <param name="source">对象</param>
        /// <param name="target">标签</param>
        public TracyRelationship AssignTag(TracyProperty property, IManagedXmlModelItem source, IManagedXmlModelItem target)
        {
            return AssignTag(property.Id, source.Id, GetTypeName(source), target.Id, GetTypeName(target));
        }

        /// <summary>
        /// 添加标签
        /// </summary>
        /// <param name="propertyId">属性ID</param>
        /// <param name="sourceId">对象ID</param>
        /// <param name="sourceType">对象数据类型</param>
        /// <param name="targetId">标签ID</param>
        /// <param name="targetType">标签数据类型</param>
        /// <returns></returns>
        public TracyRelationship AssignTag(int propertyId, int sourceId, string sourceType, int targetId, string targetType)
        {
            //验证类型
            TracyProperty prop = (TracyProperty)GetProperties().GetItemById(propertyId);
            if (prop == null) return null;
            //TODO: verify sub type
            bool valid = false;
            foreach (TracyDataType t in prop.SourceTypes)
                if (t.Name == sourceType) { valid = true; break; }
            if (!valid) return null;

            valid = false;
            foreach (TracyDataType t in prop.TargetTypes)
                if (t.Name == targetType) { valid = true; break; }
            if (!valid) return null;

            //Check Dup
            foreach (TracyRelationship existingRelationship in GetRelationshipsBySource(sourceId, sourceType))
            {
                if (targetId == existingRelationship.TargetId && targetType == existingRelationship.TargetType)
                {
                    //Tag already assigned
                    return null;
                }
            }
            //Assign Tag
            TracyRelationship newRelationship = new TracyRelationship(propertyId,sourceId,sourceType,targetId,targetType);

            try
            {
                rwLockRelationship.AcquireWriterLock(RWLOCK_TIMEOUT);
                RelationshipProvider.AddItem(newRelationship);
            }
            finally
            {
                rwLockRelationship.ReleaseWriterLock();
            }
            return newRelationship;
        }

        /// <summary>
        /// 移除标签
        /// </summary>
        /// <param name="source">对象</param>
        /// <param name="target">标签</param>
        public void RemoveTag(IManagedXmlModelItem source, IManagedXmlModelItem target)
        {
            RemoveTag(source.Id, GetTypeName(source), target.Id, GetTypeName(target));
        }

        /// <summary>
        /// 移除标签
        /// </summary>
        /// <param name="sourceId">对象ID</param>
        /// <param name="sourceType">对象数据类型</param>
        /// <param name="targetId">标签ID</param>
        /// <param name="targetType">标签数据类型</param>
        public void RemoveTag(int sourceId, string sourceType, int targetId, string targetType)
        {
            foreach (TracyRelationship existingRelationship in GetRelationshipsBySource(sourceId, sourceType))
            {
                if (targetId == existingRelationship.TargetId && targetType == existingRelationship.TargetType)
                {
                    RemoveTag(existingRelationship);
                }
            }
        }

        /// <summary>
        /// 移除标签
        /// </summary>
        /// <param name="relationshipid">关系ID</param>
        public void RemoveTag(int relationshipid)
        {
            RemoveTag((TracyRelationship)GetRelationships().GetItemById(relationshipid));
        }

        /// <summary>
        /// 移除标签
        /// </summary>
        /// <param name="relationship">关系</param>
        public void RemoveTag(TracyRelationship relationship)
        {           
            try
            {
                rwLockRelationship.AcquireWriterLock(RWLOCK_TIMEOUT);
                RelationshipProvider.RemoveItem(relationship);
            }
            finally
            {
                rwLockRelationship.ReleaseWriterLock();
            }
        }

        public void SaveSettings()
        {
            try
            {
                rwLockProperty.AcquireReaderLock(RWLOCK_TIMEOUT);
                rwLockRelationship.AcquireReaderLock(RWLOCK_TIMEOUT);
                SaveFile(PropertyProvider, Constants.CONST_SETTING_TRACYPROPERTY);
                SaveFile(RelationshipProvider, Constants.CONST_SETTING_TRACYRELATIONSHIP);
            }
            finally
            {
                rwLockProperty.ReleaseReaderLock();
                rwLockRelationship.ReleaseReaderLock();
            }
        }

        private void SaveFile(IXmlModelProvider provider, string locationName)
        {
            string output = provider.ToXml();
            TracyEngine.SaveFile(Bus.TracySettings[locationName], output);
        }
    }
}
